/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-08-14     RiceChen     first version
 */

#include "drv_usart.h"

enum
{
#ifdef BSP_USING_UART1
    UART1_INDEX,
#endif

#ifdef BSP_USING_UART2
    UART2_INDEX,
#endif

#ifdef BSP_USING_UART3
    UART3_INDEX,
#endif

#ifdef BSP_USING_UART4
    UART4_INDEX,
#endif

#ifdef BSP_USING_UART5
    UART5_INDEX,
#endif

#ifdef BSP_USING_UART6
    UART6_INDEX,
#endif

#ifdef BSP_USING_UART7
    UART7_INDEX,
#endif

#ifdef BSP_USING_LPUART1
    LPUART1_INDEX,
#endif
    UART_INDEX_MAX,
};


static struct mm32_uart_config uart_config[] =
{
#ifdef BSP_USING_UART1
    UART1_CONFIG,
#endif
#ifdef BSP_USING_UART2
    UART2_CONFIG,
#endif
#ifdef BSP_USING_UART3
    UART3_CONFIG,
#endif
#ifdef BSP_USING_UART4
    UART4_CONFIG,
#endif
#ifdef BSP_USING_UART5
    UART5_CONFIG,
#endif
#ifdef BSP_USING_UART6
    UART6_CONFIG,
#endif
#ifdef BSP_USING_UART7
    UART7_CONFIG,
#endif
#ifdef BSP_USING_LPUART1
    LPUART1_CONFIG,
#endif
};

#define UART_OBJ_SIZE       sizeof(uart_config) / sizeof(uart_config[0])

static struct mm32_uart uart_obj[UART_OBJ_SIZE] = {0};

static rt_err_t mm32_configure(struct rt_serial_device *serial,
                               struct serial_configure *cfg)
{
    struct mm32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);

    uart = rt_container_of(serial, struct mm32_uart, serial);

    uart->handle.ClockFreqHz = uart->config->clock_freq;
    uart->handle.BaudRate    = cfg->baud_rate;
    uart->handle.XferMode    = UART_XferMode_RxTx;
    uart->handle.WordLength  = UART_WordLength_8b;

    switch (cfg->stop_bits)
    {
        case STOP_BITS_1:
            uart->handle.StopBits   = UART_StopBits_1;
            break;
        case STOP_BITS_2:
            uart->handle.StopBits   = UART_StopBits_2;
            break;
        default:
            uart->handle.StopBits   = UART_StopBits_1;
            break;
    }

    switch (cfg->parity)
    {
        case PARITY_NONE:
            uart->handle.Parity     = UART_Parity_None;
            break;
        case PARITY_ODD:
            uart->handle.Parity     = UART_Parity_Odd;
            break;
        case PARITY_EVEN:
            uart->handle.Parity     = UART_Parity_Even;
            break;
        default:
            uart->handle.Parity     = UART_Parity_None;
            break;
    }

    switch (cfg->flowcontrol)
    {
        case RT_SERIAL_FLOWCONTROL_NONE:
            uart->handle.HwFlowControl = UART_HwFlowControl_None;
            break;
        case RT_SERIAL_FLOWCONTROL_CTSRTS:
            uart->handle.HwFlowControl = UART_HwFlowControl_RTS_CTS;
            break;
        default:
            uart->handle.HwFlowControl = UART_HwFlowControl_None;
            break;
    }

    UART_Init(uart->config->uartx, &uart->handle);
    UART_Enable(uart->config->uartx, true);

    return RT_EOK;
}

static rt_err_t mm32_control(struct rt_serial_device *serial,
                            int                      cmd,
                            void                     *arg)
{
    struct mm32_uart *uart;
    rt_ubase_t ctrl_arg = (rt_ubase_t)arg;

    RT_ASSERT(serial != RT_NULL);

    uart = rt_container_of(serial, struct mm32_uart, serial);

    switch (cmd)
    {
        case RT_DEVICE_CTRL_SET_INT:
        {
            UART_EnableInterrupts(uart->config->uartx, UART_INT_RX_DONE, true);
            NVIC_EnableIRQ(uart->config->irq_type);
            break;
        }
        case RT_DEVICE_CTRL_CLR_INT:
        {
            UART_EnableInterrupts(uart->config->uartx, UART_INT_RX_DONE, false);
            NVIC_DisableIRQ(uart->config->irq_type);
            break;
        }

    }
    return 0;
}

static int mm32_putc(struct rt_serial_device *serial, char c)
{
    struct mm32_uart *uart;

    RT_ASSERT(serial != RT_NULL);

    uart = rt_container_of(serial, struct mm32_uart, serial);

    while (0u == (UART_STATUS_TX_EMPTY & UART_GetStatus(uart->config->uartx)));
    UART_PutData(uart->config->uartx, (uint8_t)(c));
    return 1;
}

static int mm32_getc(struct rt_serial_device *serial)
{
    struct mm32_uart *uart;
    int ch = -1;

    RT_ASSERT(serial != RT_NULL);

    uart = rt_container_of(serial, struct mm32_uart, serial);

    if(UART_STATUS_RX_DONE & UART_GetStatus(uart->config->uartx))
    {
        ch = UART_GetData(uart->config->uartx);
        return ch;
    }
    return -1;
}

static rt_size_t mm32_dma_transmit(struct rt_serial_device *serial,
                                   rt_uint8_t              *buf,
                                   rt_size_t               size,
                                   int                     direction)
{
    return 0;
}

struct rt_uart_ops mm32_uart_ops =
{
    .configure    = mm32_configure,
    .control      = mm32_control,
    .putc         = mm32_putc,
    .getc         = mm32_getc,
    .dma_transmit = mm32_dma_transmit
};

static void uart_isr(struct rt_serial_device *serial)
{
    struct mm32_uart *uart;

    RT_ASSERT(serial != RT_NULL);

    uart = rt_container_of(serial, struct mm32_uart, serial);

    if((0u != (UART_INT_RX_DONE & UART_GetEnabledInterrupts(uart->config->uartx)))
        && (0u != (UART_INT_RX_DONE & UART_GetInterruptStatus(uart->config->uartx))))
    {
        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
    }
}


void UART1_IRQHandler(void)
{
    rt_interrupt_enter();

    uart_isr(&(uart_obj[UART1_INDEX].serial));

    rt_interrupt_leave();
}

static int rt_hw_uart_gpio_init(struct mm32_uart_config *cfg)
{
    GPIO_Init_Type gpio_init;

    RCC_EnableAPB2Periphs(cfg->uart_rcc_clock, true);
    RCC_ResetAPB2Periphs(cfg->uart_rcc_clock);

    RCC_EnableAHB1Periphs(cfg->rx_rcc_clock, true);
    RCC_ResetAHB1Periphs(cfg->rx_rcc_clock);

    RCC_EnableAHB1Periphs(cfg->tx_rcc_clock, true);
    RCC_ResetAHB1Periphs(cfg->tx_rcc_clock);

    gpio_init.Pins  = cfg->rx_pin;
    gpio_init.PinMode  = GPIO_PinMode_In_Floating;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(cfg->rx_gpiox, &gpio_init);
    GPIO_PinAFConf(cfg->rx_gpiox, cfg->rx_pin, cfg->rx_gpio_af);

    gpio_init.Pins  = cfg->tx_pin;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(cfg->tx_gpiox, &gpio_init);
    GPIO_PinAFConf(cfg->tx_gpiox, cfg->tx_pin, cfg->tx_gpio_af);
    return RT_EOK;
}

int rt_hw_uart_init(void)
{
    rt_err_t result = 0;
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    

    for(int index = 0; index < UART_OBJ_SIZE; index++)
    {
        rt_hw_uart_gpio_init(&uart_config[index]);

        uart_obj[index].config = &uart_config[index];
        
        uart_obj[index].serial.ops = &mm32_uart_ops;
        uart_obj[index].serial.config = config;

        result = rt_hw_serial_register(&uart_obj[index].serial,
                                        uart_obj[index].config->name,
                                        RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
                                        NULL);
    }

    return result;
}
